class TimeCode:
    def __init__(self, hour=0, minute=0, second=0, frame=0, rate=30):
        if hour < 0 or minute < 0 or second < 0 or frame < 0 or rate <=0:
            raise ValueError("timecode cannot be negative")
        self.hour = hour
        self.minute = minute
        self.second = second
        self.frame = frame
        self.rate = rate
        

    @staticmethod
    def format_number(number):
        return str(number).zfill(2)

    def __repr__(self) -> str:
        return f"{self.format_number(self.hour)}:{self.format_number(self.minute)}:{self.format_number(self.second)}:{self.format_number(self.frame)}"

    def to_frames(self):
        return (
            self.hour * 3600 + self.minute * 60 + self.second
        ) * self.rate + self.frame
        
    @staticmethod
    def from_frame(total_frame, frame_rate=30):
        hour = total_frame // (3600* frame_rate)
        minute = (total_frame % (3600 * frame_rate)) // (60*frame_rate)
        second = (total_frame % (60 * frame_rate)) // frame_rate
        frame = total_frame % frame_rate
        return TimeCode(hour, minute, second, frame, frame_rate)
    
    def __add__(self, other) -> "TimeCode":
        
        if isinstance(other, TimeCode):
            if self.rate != other.rate:
                raise ValueError("cannot operate between timecode of different frame rate")
            total_frame = self.to_frames() + other.to_frames()
        if isinstance(other, int):
            total_frame = self.to_frames() + other
            
        return self.from_frame(total_frame, self.rate)
    
    def __sub__(self, other) -> "TimeCode":
        if isinstance(other, TimeCode):
            if self.rate != other.rate:
                raise ValueError("cannot operate between timecode of different frame rate")
            total_frame = self.to_frames() - other.to_frames()
        if isinstance(other, int):
            total_frame = self.to_frames() - other
        if total_frame < 0:
            raise ValueError("timecode cannot be negative")
        return self.from_frame(total_frame, self.rate)
    
    @staticmethod
    def from_string(time_string: str, frame_rate):
        strings = time_string.split(":")
        return TimeCode(int(strings[0]), int(strings[1]), int(strings[2]), int(strings[3]), frame_rate)
    
    def __lt__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() < other.to_frames()
    
    def __gt__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() > other.to_frames()
    
    def __le__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() <= other.to_frames()
    
    def __ge__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() >= other.to_frames()
    
    def __eq__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() == other.to_frames()
    
    def __ne__(self, other: 'TimeCode'):
        if self.rate != other.rate:
            raise ValueError("cannot operate between timecode of different frame rate")
        return self.to_frames() != other.to_frames()
